package compare

import (
	"bytes"
	"encoding/json"
	"github.com/duke-git/lancet/v2/convertor"
	"reflect"
	"time"
)

// 比较两个值
func compareValue(operator string, left, right any) bool {
	leftType, rightType := reflect.TypeOf(left), reflect.TypeOf(right)
	if leftType.Kind() != rightType.Kind() {
		return false
	}
	switch leftType.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64,
		reflect.Bool, reflect.String:
		return compareBasicValue(operator, left, right)
	case reflect.Struct, reflect.Slice, reflect.Map:
		return compareReferenceValue(operator, left, right, leftType.Kind())
	default:
		return false
	}
}

// 引用类型比较
func compareReferenceValue(operator string, left, right any, kind reflect.Kind) bool {
	leftVal, rightVal := reflect.ValueOf(left), reflect.ValueOf(right)

	switch kind {
	case reflect.Struct:

		// compare time
		if leftVal.CanConvert(timeType) {
			timeObj1, ok := left.(time.Time)
			if !ok {
				timeObj1 = leftVal.Convert(timeType).Interface().(time.Time)
			}

			timeObj2, ok := right.(time.Time)
			if !ok {
				timeObj2 = rightVal.Convert(timeType).Interface().(time.Time)
			}

			return compareBasicValue(operator, timeObj1.UnixNano(), timeObj2.UnixNano())
		}

		// for other struct type, only process equal operator
		switch operator {
		case equal:
			return objectAraEqualValues(left, right)
		}
	case reflect.Slice:
		// compare []byte
		if leftVal.CanConvert(bytesType) {
			bytesObj1, ok := left.([]byte)
			if !ok {
				bytesObj1 = leftVal.Convert(bytesType).Interface().([]byte)
			}
			bytesObj2, ok := right.([]byte)
			if !ok {
				bytesObj2 = rightVal.Convert(bytesType).Interface().([]byte)
			}

			switch operator {
			case equal:
				if bytes.Equal(bytesObj1, bytesObj2) {
					return true
				}
			case lessThan:
				if bytes.Compare(bytesObj1, bytesObj2) == -1 {
					return true
				}
			case greaterThan:
				if bytes.Compare(bytesObj1, bytesObj2) == 1 {
					return true
				}
			case lessOrEqual:
				if bytes.Compare(bytesObj1, bytesObj2) <= 0 {
					return true
				}
			case greaterOrEqual:
				if bytes.Compare(bytesObj1, bytesObj2) >= 0 {
					return true
				}
			}

		}

		// for other type slice, only process equal operator
		switch operator {
		case equal:
			return reflect.DeepEqual(left, right)
		}
	case reflect.Map:
		// only process equal operator
		switch operator {
		case equal:
			return reflect.DeepEqual(left, right)
		}
	default:
		panic("unhandled default case")
	}
	return false
}

// 对象是否相等
func objectAraEqualValues(expected, actual interface{}) bool {
	if objectAreEqual(expected, actual) {
		return true
	}
	actType := reflect.TypeOf(actual)
	if actType == nil {
		return false
	}
	expValue := reflect.ValueOf(expected)
	if expValue.IsValid() && expValue.Type().ConvertibleTo(actType) {
		return reflect.DeepEqual(expValue.Convert(actType).Interface(), actual)
	}
	return false

}

// 比较对象是否相等
func objectAreEqual(expected, actual interface{}) bool {
	if expected == nil || actual == nil {
		return expected == actual
	}
	exp, ok := expected.([]byte)
	if !ok {
		return reflect.DeepEqual(expected, actual)
	}
	act, okAct := actual.([]byte)
	if !okAct {
		return false
	}
	if exp == nil || act == nil {
		return exp == nil && act == nil
	}
	return bytes.Equal(exp, act)
}

// 基础类型比较
func compareBasicValue(operator string, leftValue, rightValue any) bool {
	if leftValue == nil && rightValue == nil && operator == equal {
		return true
	}
	switch leftVal := leftValue.(type) {
	case json.Number:
		if left, err := leftVal.Float64(); err == nil {
			switch rightVal := rightValue.(type) {
			case json.Number:
				right, err := rightVal.Float64()
				if err != nil {
					return false
				}
				switch operator {
				case equal:
					return left == right
				case lessThan:
					return left < right
				case greaterThan:
					return left > right
				case lessOrEqual:
					return left <= right
				case greaterOrEqual:
					return left >= right
				}
			case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
				right, err := convertor.ToFloat(rightValue)
				if err != nil {
					return false
				}
				switch operator {
				case equal:
					return left == right
				case lessThan:
					return left < right
				case greaterThan:
					return left > right
				case lessOrEqual:
					return left <= right
				case greaterOrEqual:
					return left >= right
				}
			}
		}
	case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
		left, err := convertor.ToFloat(leftValue)
		if err != nil {
			return false
		}
		switch rightVal := rightValue.(type) {
		case json.Number:
			right, err := rightVal.Float64()
			if err != nil {
				return false
			}
			switch operator {
			case equal:
				return left == right
			case lessThan:
				return left < right
			case greaterThan:
				return left > right
			case lessOrEqual:
				return left <= right
			case greaterOrEqual:
				return left >= right
			}
		case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
			right, err := convertor.ToFloat(rightValue)
			if err != nil {
				return false
			}

			switch operator {
			case equal:
				return left == right
			case lessThan:
				return left < right
			case greaterThan:
				return left > right
			case lessOrEqual:
				return left <= right
			case greaterOrEqual:
				return left >= right
			}
		}
	case string:
		left := leftVal
		switch right := rightValue.(type) {
		case string:
			switch operator {
			case equal:
				return left == right
			case lessThan:
				return left < right
			case greaterThan:
				return left > right
			case lessOrEqual:
				return left <= right
			case greaterOrEqual:
				return left >= right
			}
		}
	case bool:
		left := leftVal
		switch right := rightValue.(type) {
		case bool:
			switch operator {
			case equal:
				return left == right
			}
		}

	}
	return false
}
